#include "Arduino.h"
//#define DEBUG 1
#if defined(ARDUINO_AVR_LINKITSMART7688)
#elif defined(__AVR_ATmega32U4__) || defined(__SAMD21G18A__)
#define CIAO TRUE
#warning ML_Message:Use Ciao
#include "ML_Message_Ciao.h"
#elif defined(ARDUINO_AVR_UNO_WIFI_DEV_ED)
#warning ML_Message:UNO WIFI
#include "ML_Message_UNO_WIFI.h"
#elif defined(ARDUINO_ARCH_ESP8266) 
#warning ML_Message:Default/ESP8266
#include "ML_Message_Default.h"
#elif defined(ARDUINO_ARCH_ESP32)
#warning ML_Message:Default/ESP32
#include "ML_Message_Default.h"
#else
#warning ML_Message:No WIFI
#include "ML_Message_NONE.h"
#endif
//#define ML_DEBUG 1
//#define ML_DEBUG_SEND 1
//#include"Base64.h"
// 0 = none , 1 = serial , 2 = DEPS , 3 = Stream
int streamType = 0;
Stream *stream = NULL;
extern void ML_Message_onMessage(int type, String title, String content) __attribute__((weak));
extern Stream* ML_Message_CreateStream() __attribute__((weak));
String ML_messageContent = "";
unsigned int ML_Message_length;
String ML_Message_streamComein;
char *nUoDiNg = "nUoDiNg";
const String str_nUoDiNg("nUoDiNg");

void _ML_Message_start(long baud)
{
	streamType = 1;
	Serial.begin(baud);
	#if defined ML_DEBUG
	Serial.println("Serial Open at:");
	Serial.println(baud);
	#endif
	stream = &Serial;
	stream->setTimeout(1);
	ML_Message_streamComein.reserve(300);
}

void ML_Message_start(long baud)
{
	if (baud != 0)
	{
		_ML_Message_start(baud);
		return;
	}
	#if defined ML_DEBUG
	Serial.begin(115200);
	Serial.println("Serial Open at:");
	Serial.println(115200);
	#endif
	ML_Message_streamComein.reserve(300);
	streamType = 2;
	if(!ML_Message_CreateStream){
		stream = new ML_DEPS;
	}else{
		stream = ML_Message_CreateStream();
	}
	stream->setTimeout(1);
}

void ML_Message_start(Stream &stream1)
{
	streamType = 3;
	stream = &stream1;
	stream->setTimeout(1);
	ML_Message_streamComein.reserve(300);
}
void ML_Message_send(int type, char* title, char* content, char* targetActorId)
{
	//Ciao.write(*socketserial," ML_Message_send++++++++++++++++++++++++ ");
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send 1");
	Serial.println(streamType);
	Serial.println(type);
	Serial.println(title);
	Serial.println(content);
	Serial.println(targetActorId);
	Serial.println(&Serial==stream);
	#endif
	int titleLength = strlen(title);
	// String str_titleLength(titleLength);
	char str_titleLength[8];
	itoa(titleLength,str_titleLength,10);
	unsigned int str_titleLengthLength = strlen(str_titleLength);
	int contentLength = strlen(content);
	//String str_contentLength(contentLength);
	char str_contentLength[8];
	itoa(contentLength,str_contentLength,10);
	int len_str_contentLength = strlen(str_contentLength);
	int targetActorIdLength = strlen(targetActorId);
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send 2");
	#endif
	char str_targetActorIdLength[8];
	itoa(targetActorIdLength,str_targetActorIdLength,10);
	int strlen_targetActorIdLength = strlen(str_targetActorIdLength);
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send 3");
	#endif
	/*
	int wholeLength = 1// type
		+str_titleLength.length()+1 + titleLength
		+str_contentLength.length()+1 + contentLength
		+str_targetActorIdLength.length()+1 + targetActorIdLength;
		*/
	//String msg = String(type) + str_titleLength + ":" + title + str_contentLength + ":" + content + str_targetActorIdLength + ":" + targetActorId;


	int wholeLength = 1 /* type */ 
		+ str_titleLengthLength
		+ 1 /* : */
		+ titleLength
		+ len_str_contentLength
		+ 1 /* : */ 
		+ contentLength
		+ strlen_targetActorIdLength
		+ 1 /* : */
		+ targetActorIdLength;
	//msg = str_nUoDiNg + String(wholeLength) + F(":") + msg;
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send 5");
	#endif
	//stream->println(msg);
	stream->print(str_nUoDiNg);
	stream->print(wholeLength);
	stream->print(":");
	stream->print(type);
	stream->print(str_titleLength);
	stream->print(":");
	stream->print(title);
	stream->print(str_contentLength);
	stream->print(":");
	stream->print(content);
	stream->print(str_targetActorIdLength);
	stream->print(":");
	stream->println(targetActorId);
	stream->flush();
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send 6");
	#endif

	/*
	int encodeLength = base64_enc_len(msg.length());
	char* output = (char*)malloc(encodeLength * sizeof(char));
	char* input = const_cast<char*>(msg.c_str());
	base64_encode(output, input, msg.length());
	stream.print(String(encodeLength));
	stream.print(':');
	stream.print(output);
	free(output);
	*/
}
int getInt(String msg, int *currentIndex)
{
	int int_currentIndex = *currentIndex;
	int spIndex = msg.indexOf(':', int_currentIndex);
	String str_int = msg.substring(int_currentIndex, spIndex);
	int ret = str_int.toInt();
	*currentIndex = spIndex + 1; // +1 => :
	return ret;
}

void ML_Message_send(int type, char* title, long content, char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +1");
	#endif
	char buff[15];
	ML_Message_send(type, title, ltoa(content,buff,10), targetActorId);
}
void ML_Message_send(int type, char* title, unsigned long content, char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +2");
	#endif
	char buff[15];
	ML_Message_send(type, title, ltoa(content,buff,10), targetActorId);
}
void ML_Message_send(int type, char* title, int content, char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +3");
	#endif
	char buff[15];
	ML_Message_send(type, title, itoa(content,buff,10), targetActorId);
}
void ML_Message_send(int type,char* title,float content,char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +4");
	#endif
	char buff[15];
	ML_Message_send(type, title, dtostrf(content,6,4,buff), targetActorId);
}
void ML_Message_send(int type,char* title,char content,char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +5");
	#endif
	char s[1];
	s[0]=content;
	ML_Message_send(type, title, s, targetActorId);
}
void ML_Message_send(int type,char* title,bool content,char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +6");
	#endif
	ML_Message_send(type, title, content?"true":"false", targetActorId);
}
void ML_Message_send(int type,String title,String content,char* targetActorId)
{
	#if defined ML_DEBUG_SEND
	Serial.println("ML_Message_send +7");
	#endif
	ML_Message_send(type, title.c_str(), content.c_str(), targetActorId);
}
bool _ML_Message_receive()
{
	
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive.");
#endif
	if (stream == NULL || streamType == 0)
	{
		return false;
	}
	if (!ML_Message_onMessage)
	{
		return false;
	}
	if (ML_Message_length <= 0)
	{
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. .1");
#endif
		if (!stream->find(nUoDiNg))
		{
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. .1.1");
#endif
			return false;
		}
		
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. .2");
#endif
		ML_Message_length = stream->parseInt() + 1;
		//String s = "ML_Message_length";//+String(ML_Message_length);
		//Ciao.write(*socketserial,s);
	}
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. .3");
#endif
	while (stream->available() && ML_Message_length > 0)
	{
		int in = stream->read();
		if (in == -1)
		{
			return false;
		}
		ML_Message_streamComein = ML_Message_streamComein + ((char)in);
		ML_Message_length--;
	}
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. 1");
#endif
	if (ML_Message_length <= 0)
	{
		ML_Message_streamComein = ML_Message_streamComein.substring(1);
		String str_msgType = ML_Message_streamComein.substring(0, 1);
		int messageType = str_msgType.toInt();
		String msg = ML_Message_streamComein.substring(1);
		/*
	const char* const_orig = msg.c_str();
	char* orig = const_cast<char*>(const_orig);
	int inputByteCount = msg.length();
	int outputByteCount = base64_dec_len(orig,inputByteCount);
	char* ch_buf =(char*) malloc(outputByteCount * sizeof(char));
	base64_decode(ch_buf, orig, inputByteCount);
	msg = String(ch_buf);
	free(ch_buf);
	*/
		int currentIndex = 0;
		int valueLength = 0;
		int nameLength = 0;
		String value;
		String name;
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. 2");
#endif
		switch (messageType)
		{
		case 0:
			nameLength = getInt(msg, &currentIndex);
			name = msg.substring(currentIndex, currentIndex + nameLength);
			currentIndex += nameLength;
			value = msg.substring(currentIndex);
			ML_messageContent = value;
			ML_Message_onMessage(messageType, name, value);
			break;
		case 1:
			valueLength = getInt(msg, &currentIndex);
			value = msg.substring(currentIndex, currentIndex + valueLength);
			ML_Message_onMessage(messageType, value, "");
			break;
		case 2:
			valueLength = getInt(msg, &currentIndex);
			value = msg.substring(currentIndex, currentIndex + valueLength);
			ML_Message_onMessage(messageType, value, "");
			break;
		default:
			ML_Message_send(1, " Unknown msgType:" + messageType, "", "\n");
			break;
		}
		ML_Message_streamComein = "";
		return true;
	}
	
#ifdef ML_DEBUG
Serial.println("_ML_Message_receive. 3");
#endif
	return false;
}
unsigned long deltatime = 0;
void ML_Message_receive()
{
	
#ifdef ML_DEBUG
	Serial.println("ML_Message_receive");
#endif
	unsigned long newdeltatime = millis();
	if(newdeltatime - deltatime>30){
		deltatime = newdeltatime;
	}else{
		return;
	}
	while (_ML_Message_receive())
	{
#ifdef ML_DEBUG
		Serial.println("ML_Message_receive LOOP");
#endif
	}
}

void serialEvent()
{
	if (streamType == 1)
	{
		ML_Message_receive();
	}
}